'''
visss core work :
    
    - BaseDaemon : the base class that provide daemon method

    - BaseController : the class inherit BaseDaemon and give a
                        controller's call method
    
    - BaseModel : the class inherit BaseDaemon and give a model's call method
'''
import stackless

from visss.objects import *
from visss import webObject


def balanceRecover(func):
    if hasattr(func,'im_func'):
        func.im_func._recovered=True
    else :
        func._recovered=True

    def _wrap(*args,**kwargs):
        self=func.im_self

        res=func(*args,**kwargs);
        self.balance=self.balance-1

        return res

    return _wrap

def callback(cbFunc):

    def wrap(func):

        def _wrap(event):
            res=func(event)
            cbFunc(event,res)

        return _wrap

    return wrap

class BaseDaemon(object):

    '''
    BaseDaemon:
        
        this class provides a daemon that only process
        one-subject event.
        when process this event , it calls the call method
        (which is being defined by the class inherit it) with
        the argument event
        And it will add 1 to self.balance.
       
        Note you may override a 'init' to init your own daemon
        And you may also override the shutdown to shutdown your self
        Futhermore you can provide a method called callback using 
            def callback(self,event,res)
            where event is the orignal event and res is the result(usually None)
        to have a callback '''


    def __init__(self,channel,manager_channel,balance_channel,subject):
        '''
        take the channel as self.channel which is usually crated by Manager
        manager_channel as the manager's channel and balance_channel as 
        the channel to callback when checks balance.
        the subject is for the subject that process
        '''
        self.channel=channel

        self.manager_channel=manager_channel

        self.balance_channel=balance_channel

        self.tasklet=stackless.tasklet(self.daemon)()
        self.tasklet.run()
        self.balance=0
        self.subject=subject

        if hasattr(self,'call') :
            self.call=balanceRecover(self.call)

            if hasattr(self,'callback'):
                self.call=callback(self.callback)(self.call)

        if hasattr(self,'init'):
            self.init()

    def daemon(self):
        '''
        the daemon which runs until Shutdown event comes.
        process event whose subject is self.subject with method call
        when event Check Balance come, it callback in balance_channel
        with whether self.balance == 0
        '''

        while True :

            event = self.channel.receive()

            if event.subject==self.subject:

                self.balance = self.balance+1
                tasklet=stackless.tasklet(self.call)(event)
                tasklet.run()

            elif event.subject=='Shutdown':
                if hasattr(self,'shutdown'):
                    self.shutdown()
                break

            elif event.subject == 'Check Balance':

                self.balance_channel.send(Event('Balance Result',balanced=not self.balance))

            stackless.schedule()

from decorator import decorator

def returnSingleList(func):

    '''
    turns the func result a list
    '''

    def _returnSingleList(func,*args,**kwargs):

        return [func(*args,**kwargs)]

    return decorator(_returnSingleList,func)

class BaseController(BaseDaemon):

    '''
    the class BaseController inherit BaseDaemon
    '''

    def __init__(self,channel,manager_channel,balance_channel):

        BaseDaemon.__init__(self,channel,manager_channel,balance_channel,'Parse Url')

    def call(self,event):
        '''
        this call calls the action and send the result
        back to the manager

        that means the action must return a iterable,
        I recommand you use yield
        and for speed please yield url first.

        Notice that there is a function called returnSingleList
        which is a decorator.
        '''

        action=getattr(self,event.action)

        assert callable(action)

        results=action(event.response,*event.args,**event.kwargs)

        for res in results:
            event=Event('',data=res)

            if isinstance(res,(str,webObject.Request,unicode)):
                event.subject='Parse Url'

            else :
                event.subject='Store Item'

            self.manager_channel.send(event)

class BaseModel(BaseDaemon):
    '''
    the class inherit BaseDaemon
    '''

    def __init__(self,channel,manager_channel,balance_channel):

        BaseDaemon.__init__(self,channel,manager_channel,balance_channel,'Store Item')

    def call(self,event):
        '''
        call action and return the result
        '''
        action = getattr(self,event.action)

        assert callable(action)

        return action(event.item,*event.args,**event.kwargs)
